#include "apue.h"

#include <sys/sem.h>
#include <sys/wait.h>

int main()
{
  int semid;
  pid_t pid;

  union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
  } arg;
  struct sembuf buf[1] = {0};

  // 创建信号量
  semid = semget(IPC_PRIVATE, 1, IPC_CREAT | S_IWUSR | S_IRUSR);
  if (semid < 0) {
    printf("semget failed.\n");
  } else {
    printf("semid=%d\n", semid);
  }

  // 初始化信号量
  arg.val = 1; /* 初始化为1 */
  if (semctl(semid, 0, SETVAL, arg) == -1) {
    printf("semctl failed.\n");
  }

  if ((pid = fork()) < 0) {
    printf("fork failed.\n");
  } else if (pid > 0) {
    // parent
    buf[0].sem_num = 0;
    buf[0].sem_op = -2; /* 获取2个资源，应该被阻塞 */
    buf[0].sem_flg = 0;
    printf("parent: want to get 2 resources...\n");
    if (semop(semid, buf, 1) == -1) {
      printf("parent: semop failed.\n");
    }
    printf("parent: got 2 resources.\n");

    wait(NULL);
    printf("child exit.\n");
    // 子进程退出后信号量操作应该撤销
    printf("parent: semval = %d.\n", semctl(semid, 0, GETVAL));
    return 0;
  } else {
    // child
    buf[0].sem_num = 0;
    buf[0].sem_op = 1; /* 增加1个资源，满足父进程的需求 */
    buf[0].sem_flg = 0;
    printf("child : add 1 resource.\n");
    if (semop(semid, buf, 1) == -1) {
      printf("child : semop failed1.\n");
    }

    printf("child : semval = %d.\n", semctl(semid, 0, GETVAL));

    buf[0].sem_op = 0; /* 等待信号量变为0 */
    if (semop(semid, buf, 1) == -1) {
      printf("child : semop failed2.\n");
    }
    printf("child : semval = %d.\n", semctl(semid, 0, GETVAL));

    buf[0].sem_op = 2;
    buf[0].sem_flg = SEM_UNDO;  /* 测试UNDO标志 */
    if (semop(semid, buf, 1) == -1) {
      printf("child : semop failed3.\n");
    }
    printf("child : semval = %d.\n", semctl(semid, 0, GETVAL));

    return 0;
  }

  return 0;
}